﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Security.Tokens;
using PSE.Framework.Common.Security.Services;

namespace PSE.ProcessManager.Common.Service
{
    //TODO: verificar se ha possibilidade de substituir pelo componente ServiceInstance da PSE
    public class ServiceProxyFactory<Tproxy> : IDisposable
    {
        private Tproxy _instance;
        private DuplexChannelFactory<Tproxy> _duplexServiceChannel;
        private BindingFactory _bindingFactory;

        public Tproxy GetDuplexChannelProxy(ClientCredentials credentials, object instanceCallBack, EndpointAddress endpoint, SecurityTokenParameters tokenParameters, bool useStsSecurity)
        {
            try
            {
                if (_duplexServiceChannel == null)
                {
                    if (_bindingFactory == null)
                        _bindingFactory = new BindingFactory();

                    //Criar o Binding
                    Binding binding;
                    string protocol = _bindingFactory.ResolveProtocol(endpoint);
                    if (useStsSecurity)
                    {
                        if (credentials == null)
                            throw new ArgumentException("credentials");

                        if (tokenParameters == null)
                            throw new ArgumentException("tokenParameters");

                        binding = _bindingFactory.CreateIssuedBinding(protocol, credentials.ServiceCertificate.DefaultCertificate.Subject.Replace("CN=", ""), tokenParameters);
                    }
                    else
                        binding = _bindingFactory.CreateBinding(protocol);

                    //criar o canal
                    _duplexServiceChannel = new DuplexChannelFactory<Tproxy>(new InstanceContext(instanceCallBack), binding, endpoint);

                    if (credentials != null)
                    {
                        //Forcar a utilizacao da credencial informada
                        //Remove o ClientCredential default.
                        _duplexServiceChannel.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
                        //Adiciona o CustomClientCredential.
                        _duplexServiceChannel.Endpoint.Behaviors.Add(credentials);
                    }

                    //Adiciona o PSE ClientEndpointBehavior
                    ClientEndpointBehavior endpointBehavior = new ClientEndpointBehavior();
                    _duplexServiceChannel.Endpoint.Behaviors.Add(endpointBehavior);

                    foreach (OperationDescription op in _duplexServiceChannel.Endpoint.Contract.Operations)
                    {
                        var dcSerializer = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
                        if (dcSerializer != null)
                            dcSerializer.MaxItemsInObjectGraph = Int32.MaxValue;
                    }

                    _instance = _duplexServiceChannel.CreateChannel(new InstanceContext(instanceCallBack));
                }

                return _instance;
            }
            catch (Exception ex)
            {
                throw new CommunicationException(String.Format("Could not connect create channel to address {0}. See inner exceptions for more details.", endpoint.Uri.ToString()), ex);
            }
        }


        #region IDisposable Members
        public void Dispose()
        {
            if (_duplexServiceChannel != null)
            {
                try
                {
                    if (_duplexServiceChannel.State != CommunicationState.Faulted)
                        _duplexServiceChannel.Close();
                }
                catch
                {
                    _duplexServiceChannel.Abort();
                }
            }
        }
        #endregion
    }
}
